﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using TreaShares.Backoffice.DataLayer;
using System.ComponentModel;

namespace TreaShares.Backoffice.Controlers
{
    [DataObjectAttribute(true)]
    public class GLManager
    {
        [DataObjectMethodAttribute(DataObjectMethodType.Select)]
        public List<GetGlEntriesResult> GetLedger(Guid clientId, DateTime periodEndDate)
        {
            if (periodEndDate != DateTime.MinValue)
            {
                using (TreasharesDataContext db = new TreasharesDataContext())
                {
                    var result = db.GetGlEntries(clientId, periodEndDate).ToList();
                    return result;
                }
            }
            else
            {
                return null;
            }
        }

        public void CalcLedger(Guid clientId, DateTime periodEndDate)
        {
            using (TreasharesDataContext db = new TreasharesDataContext())
            {
                var glTransactions = db.Transactions.Where(t => t.ClientId == clientId && t.ValueDate <= periodEndDate && t.TransactionType == "Stock").ToList();
                foreach (var glTran in glTransactions)
                {
                    if (!glTran.GLHeaderId.HasValue)
                    {
                        if (glTran.BuySell == 'B')
                        {
                            GLHeader glHeader = new GLHeader() { EventDescription = "On market purchase" };
                            db.GLHeaders.InsertOnSubmit(glHeader);
                            db.SubmitChanges();
                            GLCollection[] glCols = {new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.TotalConsideration.Value), ChartAccount = "Cash" },
                                                    new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.TotalConsideration.Value), ChartAccount = "Treasury Shares" }};
                            db.GLCollections.InsertAllOnSubmit(glCols);
                            glTran.GLHeaderId = glHeader.Id;
                            db.SubmitChanges();
                        }
                        else if (glTran.BuySell == 'S')
                        {
                            var counterPary = glTran.CounterParty1;
                            if (glTran.Price == 0 && counterPary.IsInternal)
                            {
                                GLHeader glHeader = new GLHeader() { EventDescription = "Cancellation of Treasury Share" };
                                db.GLHeaders.InsertOnSubmit(glHeader);
                                db.SubmitChanges();
                                double parValue = glTran.Stock1.ParValue ?? 0;
                                double weightedAverageTotalCost = this.CalcWeightedAverageTotalCost(glTransactions, glTran.ValueDate.Value);
                                GLCollection[] glCols = {
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * parValue), ChartAccount = "Paid up Capital - Par Value" },
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(weightedAverageTotalCost*glTran.Quantity) -  Math.Abs(glTran.Quantity * parValue), ChartAccount = "Paid in Captial - Excess of Par Value" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity *weightedAverageTotalCost), ChartAccount = "Treasury Shares" }};
                                db.GLCollections.InsertAllOnSubmit(glCols);
                                glTran.GLHeaderId = glHeader.Id;
                                db.SubmitChanges();
                            }
                            else if (counterPary.IsInternal && counterPary.OptionPlan)
                            {
                                GLHeader glHeader = new GLHeader() { EventDescription = "Exercise of Employee Stock Option" };
                                db.GLHeaders.InsertOnSubmit(glHeader);
                                db.SubmitChanges();
                                double weightedAverageTotalCost = this.CalcWeightedAverageTotalCost(glTransactions, glTran.ValueDate.Value);
                                GLCollection[] glCols = {
                                                    new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * weightedAverageTotalCost), ChartAccount = "Treasury Shares" },
                                                    new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * glTran.Price.Value), ChartAccount = "Cash" },
                                                    new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * Math.Abs(weightedAverageTotalCost - glTran.Price.Value)), ChartAccount = "Paid in Capital - Excess of Par Value" }};
                                db.GLCollections.InsertAllOnSubmit(glCols);
                                glTran.GLHeaderId = glHeader.Id;
                                db.SubmitChanges();
                            }
                            else if (counterPary.IsInternal)
                            {
                                GLHeader glHeader = new GLHeader() { EventDescription = "Cancellation of Treasury Share" };
                                db.GLHeaders.InsertOnSubmit(glHeader);
                                db.SubmitChanges();
                                double weightedAverageTotalCost = this.CalcWeightedAverageTotalCost(glTransactions, glTran.ValueDate.Value);
                                GLCollection[] glCols = {
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * weightedAverageTotalCost) , ChartAccount = "Shareholders Equity" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * glTran.Price.Value), ChartAccount = "Cash" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * (weightedAverageTotalCost-glTran.Price.Value)), ChartAccount = "Shareholders Equity" }};
                                db.GLCollections.InsertAllOnSubmit(glCols);
                                glTran.GLHeaderId = glHeader.Id;
                                db.SubmitChanges();
                            }
                            else
                            {
                                GLHeader glHeader = new GLHeader() { EventDescription = "On market sale" };
                                db.GLHeaders.InsertOnSubmit(glHeader);
                                db.SubmitChanges();
                                double weightedAverageTotalCost = this.CalcWeightedAverageTotalCost(glTransactions, glTran.ValueDate.Value);
                                GLCollection[] glCols;
                                if (weightedAverageTotalCost > glTran.Price.Value)
                                {
                                    // sold by loss 
                                    glCols = new GLCollection[] {
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * weightedAverageTotalCost), ChartAccount = "Shareholders Equity" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * (weightedAverageTotalCost - glTran.Price.Value )), ChartAccount = "Paid in Capital - Excess of Par Value" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR =  Math.Abs(glTran.Quantity * glTran.Price.Value), ChartAccount = "Cash" }};
                                }
                                else
                                {
                                    // sold by gain
                                    glCols = new GLCollection[] {
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * weightedAverageTotalCost), ChartAccount = "Treasury Shares" },
                                                            new GLCollection() { DebtCredit = "CR", GLHeaderId = glHeader.Id, ValueCR = Math.Abs(glTran.Quantity * (glTran.Price.Value -  weightedAverageTotalCost)), ChartAccount = "Paid in Capital" },
                                                            new GLCollection() { DebtCredit = "DR", GLHeaderId = glHeader.Id, ValueDR = Math.Abs(glTran.Quantity * glTran.Price.Value), ChartAccount = "Cash" }};
                                }
                                db.GLCollections.InsertAllOnSubmit(glCols);
                                glTran.GLHeaderId = glHeader.Id;
                                db.SubmitChanges();

                            }
                        }
                    }
                }
            }
        }

        private double CalcWeightedAverageTotalCost(List<Transaction> glTransactions, DateTime valueDate)
        {

            double weightedAverageTotalCost = 0;
            double totalBuyShares = 0;
            foreach (var transaction in glTransactions)
            {
                if (transaction.ValueDate.Value <= valueDate)
                {
                    weightedAverageTotalCost += (transaction.BuySell == 'B') ? transaction.TotalConsideration.Value : transaction.TotalFees.Value;
                    totalBuyShares += (transaction.BuySell == 'B') ? transaction.Quantity : 0;
                }
            }
            return (totalBuyShares == 0) ? 0 : Math.Abs(weightedAverageTotalCost / totalBuyShares);
        }
    }
    public class GLEntry : Transaction
    {
        public string EventDescription { get; set; }
    }
}